package org.openjava.jsonbuilder;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.openjava.utils.ReflectionUtils;
import org.openjava.utils.StringUtils;

public class PojoTree {
	private PojoNode root;
	private PojoNodeConfig config;
	public  Map<String,PojoNode> nodeMap = new HashMap<String, PojoNode>();
	
	public PojoTree(Object obj) {
		root = new PojoNode("root",obj,"");
		nodeMap.put("", root);
		initTree(root);
	}
	
	public PojoTree(Object object,PojoNodeConfig config) {
		// TODO Auto-generated constructor stub
		root = new PojoNode("root",object,"");
		this.config = config;
		nodeMap.put("", root);
		initTree(root);
	}
	
	protected  void initTree(PojoNode node){
		Object obj = node.getElement();
		Method[] methods =obj.getClass().getDeclaredMethods();
		String name = null;
		if(ReflectionUtils.isJSONMinObj(obj))
			return;
		if(node.getNodeName().equals("contact")){
			System.out.println();;
		}
		for(int i=0, j = methods.length;i < j;i ++){
			if((name = methods[i].getName()).indexOf("get") == 0 
					&& methods[i].getParameterTypes().length == 0
					&& methods[i].getModifiers() == Modifier.PUBLIC
			){ 
				String property =  StringUtils.unCapitalize(name.substring(3));
					Object object = ReflectionUtils.invokeMethod(obj, methods[i]);
					if(object != null){
						PojoNode added = node.addChild(property, object,this);
						if(added != null){ 
							nodeMap.put(added.getQuatifiedName(), added);
						}
					}
			}
		}
	}
	
	
	public PojoNode getRoot() {
		return root;
	}
	
	public PojoNodeConfig getConfig() {
		return config;
	}
	
	public PojoNode[] getNodesByQuatifiedName(String name){
		if(name.indexOf("!") != -1){
			return getNodesByQuatifiedNameWithPlaint(name);
		}
	 	int index = -1;
	 	PojoNodeConfig config  = new PojoNodeConfig(name);
		String[] metas = name.split(";");
		String front = null;
		String later = null;
		int begin = 0;
		int end = 0;
		List<PojoNode> selectedNodes = new ArrayList<PojoNode>();
		for(String meta:metas){
			do{
				index = meta.indexOf("*", index + 1);
				if(index < 0)
					break;
				begin = end;
				end = index;
				front = meta.substring(begin,end);
				if(((end == 0 && meta.length() == 1)|| end == meta.length() -1))  {
					later = "";
				}else
					later = meta.substring(end + 1);
				if(!front.equals("")){
					front =front.substring(0, front.length() -1);
				}
				
				if(!config.isIncluded(front)){
					break;
				}
				PojoNode nod = this.nodeMap.get(front);
				if(nod == null){
					break;
				}
				List<PojoNode> children = nod.getChildren();
				for(PojoNode node:children){
					String quatName = node.getQuatifiedName();
					PojoNode[] nn = null;
					if(later.equals("")){
						selectedNodes.add(node);
					}else{
						selectedNodes.add(node);
						nn = getNodesByQuatifiedName(quatName + later);
						for(PojoNode n:nn){
							selectedNodes.add(n);
						}
					}
				}
			}while(index >=0);
			
			if(meta.indexOf("*") == -1){
				selectedNodes.add(this.nodeMap.get(meta));
			}
		}
		return selectedNodes.toArray(new PojoNode[0]);
	}
	
	public PojoNode[] getNodesByQuatifiedNameWithPlaint(String name){
		PojoNodeConfig config = new PojoNodeConfig(name);
		 List<PojoNode> result = filterChildren(root, config);
		 PojoNode[] nodes = new PojoNode[result.size()];
		 return (PojoNode[])result.toArray(nodes);
	}
	
	private List<PojoNode> filterChildren(PojoNode node,PojoNodeConfig quatifiedName){
		PojoNodeConfig config = quatifiedName;
		List<PojoNode> validChildren = new ArrayList<PojoNode>();
		List<PojoNode> children = node.getChildren();
		for(int i = 0,j = children.size();i <j;i ++){
			PojoNode child = children.get(i);
			
			int degree = config.fitDegree(child.getQuatifiedName());
			
			if(degree == PojoNodeConfig.PERFECT_FIT){
				validChildren.add(child);
			}else if(degree == PojoNodeConfig.PART_FIT){
				List<PojoNode> list = filterChildren(child, quatifiedName); 
				for(int i0=0,j0 = list.size();i0 < j0;i0 ++){
					validChildren.add(list.get(i0));
				}
			}
		}
		return   validChildren ;
	}
	
	public boolean deleteNode(String quatifiedName){
		PojoNode[] nodes = this.getNodesByQuatifiedName(quatifiedName);
		boolean success = true;
		for(int i = 0,j = nodes.length;i <j;i ++){
			if(!nodes[i].getParent().removeChild(nodes[i])){
				success = false;
			}
		}
		return success;
	}
	
	public boolean deleteNode(PojoNode node){
		return node.getParent().removeChild(node);
	}
	
	public boolean deleteNodes(Collection<PojoNode> nodes){
		boolean success = true;
		Iterator<PojoNode> ns = nodes.iterator();
		while(ns.hasNext()){
			PojoNode node = ns.next();
			if(!node.getParent().removeChild(node)){
				success = false;
			}
		}
		return success;
		
	}
	
	public boolean replaceNode(PojoNode node,String quatifiedName){
		PojoNode[] nodes = this.getNodesByQuatifiedName(quatifiedName);
		if(nodes.length == 0){
			return false;
		}
		List<PojoNode> children = null;
		for(int i = 0,j = nodes.length;i < j;i ++){
			 children = nodes[i].getParent().getChildren();
			int index = children.indexOf(nodes[i]);
			children.set(index, node);
		}
		return true;
	}
	
	public String toJSON(){
		List<PojoNode> children = root.getChildren();
		StringBuffer buffer = new StringBuffer();
		buffer.append("{");
		root.setBuffer(buffer);
		for(PojoNode node:children){
			node.toJSON();
			buffer.append(",");
		}
		buffer.deleteCharAt(buffer.length() -1);
		buffer.append("}");
		return this.root.getBuffer().toString();
		
	}
	public static void main(String[] args) {
		org.openjava.model.Contact contact = new org.openjava.model.Contact();
		contact.setAddress("234");
		contact.setTelephone("12323");
		contact.setZipcode("323300");
		org.openjava.model.People p = new org.openjava.model.People();
		p.setAge(22);
		p.setContact(contact);
		p.setDate(new Date());
		p.setName("LeiTing");
		p.setSex("male");
		p.setTest(new int[]{1,2,3});
		
		org.openjava.model.Contact contact2 = new org.openjava.model.Contact();
		contact2.setAddress("234");
		contact2.setTelephone("12323");
		contact2.setZipcode("323300");
		org.openjava.model.People p2 = new org.openjava.model.People();
		p2.setAge(22);
		p2.setContact(contact2);
		p2.setDate(new Date());
		p2.setName("LeiTing");
		p2.setSex("male");
		p2.setTest(new int[]{1,2,3});
		
		p.setFather(p2);
		PojoNodeConfig config = new PojoNodeConfig();
		config.setConfig("*");
		config.setQuote("'");
		PojoTree tree = new PojoTree(p,config);
		PojoNode[] nodes = tree.getNodesByQuatifiedName("*.*.*;");
		long start = System.currentTimeMillis();
		for(int i = 0;i < 10000;i ++){
			tree.toJSON();
		}
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
}
